#include "Scene_PCH.h"
#include "WorldMgr.h"

/*--------------------------------------------------------------------------*/

// struct	DbvtBroadphaseFrustumCulling : btDbvt::ICollide	{
// 	btAlignedObjectArray<btCollisionObject*>* m_pCollisionObjectArray;
// 	short int m_collisionFilterMask;
// 	btCollisionObject* m_additionalCollisionObjectToExclude;	// Unused in this demo
// 	OcclusionBuffer* m_ocb;
// 
// 	DbvtBroadphaseFrustumCulling(btAlignedObjectArray<btCollisionObject*>* _pArray=NULL) 
// 		: 	m_pCollisionObjectArray(_pArray),m_collisionFilterMask(btBroadphaseProxy::AllFilter & ~btBroadphaseProxy::SensorTrigger),m_additionalCollisionObjectToExclude(NULL),m_ocb(NULL)
// 	{
// 	}
// 	bool	Descent(const btDbvtNode* node)	{
// 		return(m_ocb->queryOccluder(node->volume.Center(),node->volume.Extents()));
// 	}						
// 	void	Process(const btDbvtNode* node,btScalar depth)	{Process(node);}
// 	void	Process(const btDbvtNode* leaf)	{	
// 		btBroadphaseProxy*	proxy=static_cast < btBroadphaseProxy*> (leaf->data);
// 		btCollisionObject* co = static_cast < btCollisionObject* > (proxy->m_clientObject);
// 		if (m_ocb && IsOccluder(co) && co->getCollisionShape()) {
// 			static btVector3 aabbMin;
// 			static btVector3 aabbMax;
// 			co->getCollisionShape()->getAabb(btTransform::getIdentity(),aabbMin,aabbMax);	// Actually here I should get the MINIMAL aabb that can be nested INSIDE the shape (ie. only btBoxShapes work)
// 			m_ocb->appendOccluder((aabbMax-aabbMin)*btScalar(0.5f),co->getWorldTransform());	// Note that only a btVector3 (the inner box shape half extent) seems to be necessary here.
// 		}				
// 		if ((proxy->m_collisionFilterGroup & m_collisionFilterMask) != 0 && co != m_additionalCollisionObjectToExclude)	{
// 			m_pCollisionObjectArray->push_back( co );
// 		}	
// 	}			
// }	g_DBFC;

/*--------------------------------------------------------------------------*/

WorldMgr::WorldMgr() :
	m_loadedLevel(NULL)
{
	m_timer = g_Kernel->CreateNewTimer();
	m_worldSync_mutex = g_Kernel->CreateNewMutex();
}

/*--------------------------------------------------------------------------*/

WorldMgr::~WorldMgr()
{
	delete m_timer;
	delete m_worldSync_mutex;

	delete m_bullet_dynamicsWorld;
	delete m_bullet_solver;
	delete m_bullet_collisionConfiguration;
	delete m_bullet_dispatcher;
	delete m_bullet_broadphase;
}

/*--------------------------------------------------------------------------*/

void WorldMgr::InitWorldMgr()
{
	m_bullet_broadphase				= new btDbvtBroadphase();
	m_bullet_collisionConfiguration = new btDefaultCollisionConfiguration();
	m_bullet_dispatcher				= new btCollisionDispatcher(m_bullet_collisionConfiguration);
	m_bullet_solver					= new btSequentialImpulseConstraintSolver;
	m_bullet_dynamicsWorld			= new btDiscreteDynamicsWorld(m_bullet_dispatcher, m_bullet_broadphase, m_bullet_solver, m_bullet_collisionConfiguration);

	LoadLevel("ireland");
}

/*--------------------------------------------------------------------------*/

void WorldMgr::SetGravity( const glm::vec3& gravity )
{
	m_bullet_dynamicsWorld->setGravity(btVector3(gravity.x, gravity.y, gravity.z));
}

/*--------------------------------------------------------------------------*/

void WorldMgr::AddBorderPlane( const std::string& name, const glm::vec3& normal, float constant )
{
	static glm::quat q = glm::quat();

	q = q;// * glm::angleAxis(35.0f, glm::vec3(1.0, 0.0, 0.0));

	bInitData initdata;
	
	initdata.position.y = 100.0f;
	initdata.position.x = 250.0f;
	initdata.position.z = 250.0f;

	initdata.restitution = 0.95f;
	initdata.mass = 1.0f;
	for(uint i = 0; i < 100; ++i)
	{
		initdata.position.y += 2;
		initdata.position.x += (rand()%1000 - 500) / 50.0f;
		initdata.position.z += (rand()%1000 - 500) / 50.0f;
		
		bMesh* mesh = new bMesh(m_bullet_dynamicsWorld, initdata);
		AddObject("asdad" + std::to_string(i), mesh);
	}
}

/*--------------------------------------------------------------------------*/

void WorldMgr::AddObject(const std::string& name, bObject* object )
{
	m_nameWithObject[name] = object;
}

/*--------------------------------------------------------------------------*/

void WorldMgr::RenderOpaque()
{
	m_worldSync_mutex->Lock();

	for (auto iter = m_nameWithObject.begin(); iter != m_nameWithObject.end(); ++iter)
	{
		iter->second->PreRendering(0);
	}

	m_worldSync_mutex->Unlock();
}

/*--------------------------------------------------------------------------*/

void WorldMgr::CalculatePhysics()
{
	m_worldSync_mutex->Lock();
	m_bullet_dynamicsWorld->stepSimulation(m_timer->GetTime_ms() * 0.001f, 20);
// 	
// 	g_DBFC.m_ocb = NULL;
// 	btDbvt::collideKDOP(m_bullet_broadphase->m_sets[1].m_root, planes_n, planes_o, 5, g_DBFC);
// 	btDbvt::collideKDOP(m_bullet_broadphase->m_sets[0].m_root, planes_n, planes_o, 5, g_DBFC);			
// 

	std::string msg = "BulletPhysics step: " + std::to_string(m_timer->GetTime_ms()) + "ms";

	g_UserInterface->AddMessage(msg);

	m_timer->ResetTimer();
	m_worldSync_mutex->Unlock();
}

/*--------------------------------------------------------------------------*/

void WorldMgr::LoadLevel( const std::string& name )
{
	__Check(m_loadedLevel == NULL);
	m_loadedLevel = new Level();
	m_loadedLevel->Load(name);
}

/*--------------------------------------------------------------------------*/

Level* WorldMgr::GetLoadedLevel()
{
	return m_loadedLevel;
}

/*--------------------------------------------------------------------------*/

btDiscreteDynamicsWorld* WorldMgr::GetWorld()
{
	return m_bullet_dynamicsWorld;
}

/*--------------------------------------------------------------------------*/
